package robot

import (
	"context"
	"encoding/json"
	"strconv"
	"time"

	"sale-admin/config/mysql"
	"sale-admin/config/redis"
	"sale-admin/internal/app/grpc/client"
	"sale-admin/internal/app/grpc/protoc/devicec"
	"sale-admin/internal/app/web/schema"
	"sale-admin/internal/app/web/service"
	"sale-admin/internal/app/web/service/common"
	"sale-admin/internal/app/web/service/coupon"
	"sale-admin/internal/app/web/service/order"
	"sale-admin/internal/app/web/service/pay"
	"sale-admin/internal/dal"

	"sale-admin/internal/dao"
	"sale-admin/internal/define"
	"sale-admin/internal/models"
	"sale-admin/internal/pkg/points"

	"192.168.1.75/go-pkg/errorx"
	"192.168.1.75/go-pkg/helper"
	"192.168.1.75/go-pkg/logx"
	"github.com/go-pay/gopay/alipay"
	"github.com/go-pay/gopay/wechat"
	"github.com/golang-module/carbon/v2"
	"github.com/samber/lo"
	"gorm.io/datatypes"
	"gorm.io/gorm"
)

type RobotService struct{}

// setGoods 设置补货
func (r RobotService) setGoods(db *gorm.DB, salesID, robotName string, params schema.SetGoodsData) (count int64, err error) {
	// 参数校验
	{
		if *params.ContainerUseStatus <= 0 {
			return 0, errorx.New("货柜状态不合法", -1)
		}

		if params.GoodsInventory < 0 || params.GoodsInventory > 20 {
			return 0, errorx.New("货柜容量不合法")
		}

		if params.GoodsMaxInventory <= 0 || params.GoodsMaxInventory > 20 {
			return 0, errorx.New("最大容量不合法")
		}

		if params.GoodsInventory > params.GoodsMaxInventory {
			params.GoodsInventory = params.GoodsMaxInventory
		}

		if !(params.ContainerNum >= 0 && params.ContainerNum < 60) {
			return 0, errorx.New("货道号不规范")
		}

		if params.GoodsID == "" {
			return 0, errorx.New("商品ID必填")
		}
	}

	// 数据写入
	{
		// 查询货道信息
		record := make(map[string]interface{})
		var salesInfoCount int64 = 0
		db.Model(&models.MaRobotSalesInfo{}).
			Where("sales_id = ?", salesID).
			Where("container_floor = ?", params.ContainerFloor).
			Where("container_num = ?", params.ContainerNum).
			Count(&salesInfoCount)
		// 存在就更新记录
		if salesInfoCount > 0 {
			if *params.ContainerType != -1 {
				record["container_type"] = *params.ContainerType
			}
			if *params.ContainerUseStatus != -1 {
				record["container_use_status"] = *params.ContainerUseStatus
			}
			if params.ClassID != "" {
				record["class_id"] = params.ClassID
			}

			record["container_status"] = params.ContainerStatus
			record["goods_id"] = params.GoodsID
			record["goods_inventory"] = params.GoodsInventory
			record["goods_max_inventory"] = params.GoodsMaxInventory
			record["add_time"] = time.Now()
			count = db.Model(&models.MaRobotSalesInfo{}).
				Where("container_floor = ?", params.ContainerFloor).
				Where("container_num = ?", params.ContainerNum).
				Where("sales_id = ?", salesID).
				Updates(record).
				RowsAffected
			if count <= 0 {
				return 0, errorx.New("操作失败", -1)
			} else {
				return 1, nil
			}
			// 不存在则新增记录
		} else {
			var containerInfo models.MaRobotSalesInfo
			containerInfo.ContainerType = *params.ContainerType
			containerInfo.ContainerUseStatus = *params.ContainerUseStatus
			containerInfo.SalesID = salesID
			containerInfo.RobotName = robotName
			containerInfo.ContainerNum = params.ContainerNum       // 编号
			containerInfo.ContainerFloor = params.ContainerFloor   // 楼层
			containerInfo.ContainerStatus = params.ContainerStatus // 货柜状态 1上架 0下架
			containerInfo.ClassID = params.ClassID                 // 标签ID
			containerInfo.GoodsID = params.GoodsID                 // 商品ID
			containerInfo.GoodsInventory = params.GoodsInventory   // 库存
			containerInfo.GoodsMaxInventory = params.GoodsMaxInventory
			containerInfo.AddTime = time.Now()
			count = db.Model(&models.MaRobotSalesInfo{}).
				Create(&containerInfo).RowsAffected
			if count <= 0 {
				return 0, errorx.New("操作失败", -1)
			} else {
				return 1, nil
			}
		}
	}
}

// setGoods 设置补货
func (r RobotService) AndriodsetGoods(db *gorm.DB, salesID string, params schema.AndriodSetGoodsData) (count int64, err error) {
	// 参数校验
	{
		if params.ContainerUseStatus != nil && *params.ContainerUseStatus <= "0" {
			return 0, errorx.New("货柜状态不合法", -1)
		}
		GoodsInventory, _ := strconv.Atoi(params.GoodsInventory)
		if GoodsInventory < 0 || GoodsInventory > 20 {
			return 0, errorx.New("货柜容量不合法")
		}

		GoodsMaxInventory, _ := strconv.Atoi(params.GoodsMaxInventory)
		if GoodsMaxInventory <= 0 || GoodsMaxInventory > 20 {
			return 0, errorx.New("最大容量不合法")
		}

		if GoodsInventory > GoodsMaxInventory {
			params.GoodsInventory = params.GoodsMaxInventory
		}

		ContainerNum, _ := strconv.Atoi(params.ContainerNum)
		if ContainerNum < 0 || ContainerNum > 60 {
			return 0, errorx.New("货道号不规范")
		}

		if params.GoodsID == "" {
			return 0, errorx.New("商品ID必填")
		}

	}

	// 数据写入
	{
		// 查询货道信息
		record := make(map[string]interface{})
		var salesInfoCount int64 = 0
		db.Model(&models.MaRobotSalesInfo{}).
			Where("sales_id = ?", salesID).
			Where("container_floor = ?", params.ContainerFloor).
			Where("container_num = ?", params.ContainerNum).
			Count(&salesInfoCount)
		// 存在就更新记录
		if salesInfoCount > 0 {
			if params.ContainerType != nil && *params.ContainerType != "-1" {
				record["container_type"] = *params.ContainerType
			}
			if params.ContainerUseStatus != nil && *params.ContainerUseStatus != "-1" {
				record["container_use_status"] = *params.ContainerUseStatus
			}
			if params.ClassID != "" {
				record["class_id"] = params.ClassID
			}

			record["container_status"] = 1
			record["goods_id"] = params.GoodsID
			record["goods_inventory"] = params.GoodsInventory
			record["goods_max_inventory"] = params.GoodsMaxInventory
			record["add_time"] = time.Now()
			count = db.Model(&models.MaRobotSalesInfo{}).
				Where("container_floor = ?", params.ContainerFloor).
				Where("container_num = ?", params.ContainerNum).
				Where("sales_id = ?", salesID).
				Updates(record).
				RowsAffected
			if count <= 0 {
				return 0, errorx.New("操作失败", -1)
			} else {
				return 1, nil
			}
			// 不存在则新增记录
		} else {
			var containerInfo models.MaRobotSalesInfo
			containerInfo.ContainerType, _ = strconv.Atoi(*params.ContainerType)
			containerInfo.ContainerUseStatus, _ = strconv.Atoi(*params.ContainerUseStatus)
			containerInfo.SalesID = salesID
			containerInfo.ContainerNum, _ = strconv.Atoi(params.ContainerNum)     // 编号
			containerInfo.ContainerFloor, _ = strconv.Atoi(params.ContainerFloor) // 楼层
			containerInfo.ContainerStatus = 1                                     // 货柜状态 1上架 0下架
			containerInfo.ClassID = params.ClassID                                // 标签ID
			containerInfo.GoodsID = params.GoodsID                                // 商品ID
			containerInfo.GoodsInventory, _ = strconv.Atoi(params.GoodsInventory) // 库存
			containerInfo.GoodsMaxInventory, _ = strconv.Atoi(params.GoodsMaxInventory)
			containerInfo.AddTime = time.Now()
			count = db.Model(&models.MaRobotSalesInfo{}).
				Create(&containerInfo).RowsAffected
			if count <= 0 {
				return 0, errorx.New("操作失败", -1)
			} else {
				return 1, nil
			}
		}
	}
}

// 获取机器补货前的数据
func (r RobotService) getRobotGoodsStatus(db *gorm.DB, robotName string) (goodsInfo map[string]schema.RobotGoodsStatus, err error) {
	var goodsData []schema.SetGoodsData // 定义一个切片
	// 获取之前的商品补货数据
	db.Table(models.MaRobotSalesInfo{}.TableName()+" as r").
		Joins("left join "+models.MaGoodsInfo{}.TableName()+" as g on g.goods_id = r.goods_id").
		Where("r.robot_name = ? and r.container_status = ? and r.container_use_status = ?", robotName, 1, 1).
		Select("r.*, g.goods_name1").
		Find(&goodsData)

	goodsIDDataMap := make(map[string][]schema.SetGoodsData) // 商品id => 机器商品数据
	for _, v := range goodsData {
		goodsIDDataMap[v.GoodsID] = append(goodsIDDataMap[v.GoodsID], v)
	}

	// 返回补货数据
	goodsInfo = make(map[string]schema.RobotGoodsStatus)
	for goodsID, data := range goodsIDDataMap { // 循环切片
		var newV schema.RobotGoodsStatus
		for _, n := range data {
			newV.SalesID = n.SalesID
			newV.RobotName = n.RobotName
			newV.GoodsNum += n.GoodsInventory // 商品数量累加!!!
			newV.PreNum = newV.GoodsNum
			newV.GoodsName1 = n.GoodsName1
			newV.GoodsID = n.GoodsID
			newV.AddTime = time.Now()
			goodsInfo[goodsID] = newV
		}
	}
	return goodsInfo, nil
}

// 获取补货后的商品数据
func (r RobotService) GetDataLastRobot(db *gorm.DB, salesID string, goodsInfoPre map[string]schema.RobotGoodsStatus, userID string) (err error) {
	var goodsData []schema.SetGoodsData
	lastTotalNum := 0 // 补货后的商品总个数
	preTotalNum := 0  // 统计补货前的商品总量

	// 查询补完货的数据
	db.Table(models.MaRobotSalesInfo{}.TableName()+" as r").
		Joins("left join "+models.MaGoodsInfo{}.TableName()+" as g on g.goods_id = r.goods_id").
		Where("r.sales_id = ? and r.container_status = 1 and r.container_use_status = 1", salesID).
		Select("r.*, g.goods_name1").
		Find(&goodsData)

	GoodsIDDataMap := make(map[string][]schema.SetGoodsData) // 商品id => 商品数据
	var RobotName string
	for _, v := range goodsData {
		GoodsIDDataMap[v.GoodsID] = append(GoodsIDDataMap[v.GoodsID], v)
		RobotName = v.RobotName
	}

	// 记录每个商品的数量, 中间相同商品需要进行累加
	compareLast := make(map[string]schema.RobotGoodsStatus)
	for _, Data := range GoodsIDDataMap {
		goodsNum := 0    // 当前商品的实际存量
		GoodsID := ""    // 当前商品的GoodsID
		GoodsName1 := "" // 当前商品的名称
		for _, v := range Data {
			goodsNum += v.GoodsInventory     // 累计
			lastTotalNum += v.GoodsInventory // 所有商品总量累加
			GoodsName1 = v.GoodsName1        // 商品名称
			GoodsID = v.GoodsID              // 商品ID
		}
		var val schema.RobotGoodsStatus
		val.SalesID = salesID
		val.GoodsNum = goodsNum
		val.GoodsName1 = GoodsName1
		val.GoodsID = GoodsID
		compareLast[GoodsID] = val
	}

	comparePre := make(map[string]schema.RobotGoodsStatus)
	for _, v := range goodsInfoPre {
		preTotalNum += v.PreNum
		comparePre[v.GoodsID] = v
	}

	// 1. 获取已经下架的商品, 变动设置为0
	var saveData []models.MaSaleInfoLogDetail
	LogID, _ := helper.UUID{}.Int64()
	// 如果差异存在
	compareDiff, _ := lo.Difference(lo.Keys(comparePre), lo.Keys(compareLast))
	if len(compareDiff) != 0 { // compareDiff comparePre中在compareLast中缺少的项
		leftVal := lo.Values(comparePre) // 获取差异的值
		for _, diffKey := range compareDiff {
			for k, val := range leftVal {
				if strconv.Itoa(k) == diffKey {
					var vs models.MaSaleInfoLogDetail
					vs.LogID = LogID
					vs.SalesID = val.SalesID
					vs.GoodsID = val.GoodsID
					vs.GoodsName = val.GoodsName
					vs.PreNum = val.PreNum
					vs.LastNum = 0
					vs.ChangeNum = -val.PreNum
					vs.AddTime = time.Now()
					saveData = append(saveData, vs)
				}
			}
		}
	}
	// 2. 把当前存在的商品进行统计
	for k, v := range compareLast {
		var vs models.MaSaleInfoLogDetail
		vs.LogID = LogID
		vs.SalesID = v.SalesID
		vs.GoodsID = v.GoodsID
		vs.AddTime = time.Now()
		if comparePre[k].GoodsName1 != "" {
			vs.GoodsName = v.GoodsName1
			vs.PreNum = comparePre[k].PreNum
			vs.LastNum = v.GoodsNum
			vs.ChangeNum = v.GoodsNum - comparePre[k].PreNum
			saveData = append(saveData, vs)
		} else {
			vs.GoodsName = v.GoodsName1
			vs.PreNum = 0
			vs.LastNum = v.GoodsNum
			vs.ChangeNum = v.GoodsNum
			saveData = append(saveData, vs)
		}
	}

	if len(saveData) > 0 {
		var setGoodsType int // 补货类型[1多2少3持平]
		setGoodsNum := lastTotalNum - preTotalNum
		if setGoodsNum > 0 {
			setGoodsType = 1
		} else if setGoodsNum < 0 {
			setGoodsType = 2
		} else if setGoodsNum == 0 {
			setGoodsType = 3
		}
		// 保存补货详细的记录
		count := db.Model(&models.MaSaleInfoLogDetail{}).Create(&saveData).RowsAffected
		if count > 0 {
			usernameMap, _ := common.GetUsernameMap(context.Background(), []string{userID})
			var logData models.MaSaleInfoLog
			logData.ID = LogID
			logData.AddTime = time.Now()
			logData.Type = setGoodsType
			logData.Num = setGoodsNum
			logData.SalesID = salesID
			logData.RobotName = RobotName
			logData.Date = datatypes.Date(time.Now())
			logData.UserID = userID
			logData.Username = usernameMap[userID]
			db.Model(&models.MaSaleInfoLog{}).Create(&logData)
		}
	}
	return
}

// existDevice 判断设备是否存在
func (r RobotService) existDevice(robotIDParam, salesIDParam string) (robotID string, salesID string, exists bool) {
	// 判断设备是否存在
	req := &devicec.DeviceDetailReq{}
	if robotIDParam != "" {
		req.RobotId = robotIDParam
	} else if salesIDParam != "" {
		req.IdentificationCode = salesIDParam
	}
	deviceClient, clientErr := client.GetDeviceAdminClient()
	if clientErr != nil {
		logx.Error(context.Background(), "grpc device client error", logx.Err(clientErr))
		return
	}

	res, err := deviceClient.DeviceDetail(context.Background(), req)
	if err != nil {
		return "", "", false
	}
	return res.Device.RobotId, res.Device.IdentificationCode, true
}

// ResetContainerStauts
// 返回上一次补货的数据，并重置货道状态
func (r RobotService) ResetContainerStauts(db *gorm.DB, robotName string, andriod int) (map[string]schema.RobotGoodsStatus, error) {
	robotGoodsStatus, err := r.getRobotGoodsStatus(db, robotName)
	if err == nil && len(robotGoodsStatus) != 0 {
		tx := db.Model(&models.MaRobotSalesInfo{}).
			Where("robot_name = ?", robotName).
			Where("container_status = ?", 1)
		if andriod == 1 {
			tx = tx.Where("container_use_status <> ?", 2) // 1正常 2删除
		}
		tx.Updates(map[string]interface{}{
			"container_status": 0,
		})
	}
	return robotGoodsStatus, err
}

// SetGoods 设置机器补货
func (r RobotService) SetGoods(ctx context.Context, db *gorm.DB, merchantID string, params schema.RobotSalesInfoParams, userID string) (count int, data map[string]bool, err error) {
	// 1.判断是否存在
	robotID, salesID, exist := r.existDevice(params.RobotID, params.SalesID)
	if !exist {
		return 0, nil, errorx.New("设备不存在", -1)
	}
	// 2.重置机器的货道状态
	robotGoodsStatusMap, err := r.ResetContainerStauts(db, robotID, 0)
	if err != nil {
		return 0, nil, err
	}
	// 3.为每个货道号补货，成功则返回当前货道是否补货成功
	data = make(map[string]bool)
	for _, containerData := range params.Data {
		count, err := r.setGoods(db, salesID, robotID, containerData)
		if err != nil {
			logx.Error(context.Background(), "set goods error", logx.Err(err))
		}
		robotContainer := robotID + "_" + strconv.Itoa(containerData.ContainerNum)
		if err == nil && count >= 0 {
			data[robotContainer] = true
		} else {
			data[robotContainer] = false
		}
	}

	// 4. 保存机器补货后的商品信息并写入补货日志
	err = r.GetDataLastRobot(db, salesID, robotGoodsStatusMap, userID)
	if err != nil {
		err = errorx.New("写入出错", -1)
		return
	}
	return len(data), data, err
}

// SetGoods 设置机器补货
func (r RobotService) AndriodSetGoods(ctx context.Context, db *gorm.DB, merchantID string, params schema.AndriodRobotSalesInfoParams, userID string) (count int, data map[string]bool, err error) {
	// 1.判断是否存在
	robotID, salesID, exist := r.existDevice("", params.SalesID)
	if !exist {
		return 0, nil, errorx.New("设备不存在", -1)
	}
	// 2.重置机器的货道状态
	robotGoodsStatusMap, err := r.ResetContainerStauts(db, robotID, 1)
	if err != nil {
		return 0, nil, err
	}
	// 3.为每个货道号补货，成功则返回当前货道是否补货成功
	data = make(map[string]bool)
	for _, containerData := range params.Data {
		count, err := r.AndriodsetGoods(db, salesID, containerData)
		if err != nil {
			logx.Error(context.Background(), "set goods error", logx.Err(err))
		}
		robotContainer := robotID + "_" + containerData.ContainerNum
		if err == nil && count >= 0 {
			data[robotContainer] = true
		} else {
			data[robotContainer] = false
		}
	}
	// 4. 保存机器补货后的商品信息并写入补货日志
	err = r.GetDataLastRobot(db, salesID, robotGoodsStatusMap, userID)
	if err != nil {
		err = errorx.New("写入出错", -1)
		return
	}
	return len(data), data, err
}

// Set 设置机器新城新店标识
func (r RobotService) SetOpening(merchant_id string, robotID string, OpeningFlag, OpeningTime string) error {
	if robotID == "" {
		return errorx.New("机器必填", -1)
	}
	db := mysql.NewDB()
	// 非平台商户，只能设置自己的设备
	if merchant_id != "1" {
		var device models.MaRobot
		rowsAffected := db.Model(&models.MaRobot{}).
			Where(models.MaRobot{
				RobotName: robotID,
			}).
			Take(&device).
			RowsAffected
		if rowsAffected <= 0 {
			return errorx.New("设备不存在", -1)
		}
	}

	// 设置新城新店
	rowsAffected := db.Model(&models.MaRobot{}).
		Where(models.MaRobot{
			RobotName: robotID,
		}).
		Updates(&models.MaRobot{
			OpeningFlag: OpeningFlag,
			OpeningTime: OpeningTime,
		}).RowsAffected
	if rowsAffected < 0 {
		return errorx.New("设置失败", -1)
	}
	return nil
}

// GetOpening 获取新城新店设置
func (r RobotService) GetOpening(robotID string) (openingFlag, openingTime string) {
	if robotID == "" {
		return "", ""
	}
	db := mysql.NewDB()
	var record models.MaRobot
	// 获取新城新店设置
	db.Model(&models.MaRobot{}).
		Where(models.MaRobot{
			RobotName: robotID,
		}).Take(&record)
	return record.OpeningFlag, record.OpeningTime
}

// 上报用户画像
func (r RobotService) UpPhysicalInfo(ctx context.Context, db *gorm.DB, Age, Sex, Type int, Msg, salesID string) (err error) {
	robotInfo, robotExist := r.CheckRobotExist(ctx, db, salesID, "")
	if !robotExist {
		err = errorx.New("找不到机器人", -1)
		return
	}
	var InsertData models.MaPhysicalInfo
	InsertData.SalesID = salesID
	InsertData.RobotName = robotInfo.RobotName
	InsertData.Msg = Msg
	InsertData.Type = Type
	InsertData.Age = Age
	InsertData.Sex = Sex
	// 计算年龄区间
	if Age >= 1 && Age <= 4 {
		InsertData.AgeRange = 4
	} else if Age >= 5 && Age <= 7 {
		InsertData.AgeRange = 7
	} else if Age >= 8 && Age <= 11 {
		InsertData.AgeRange = 11
	} else if Age >= 12 && Age <= 19 {
		InsertData.AgeRange = 19
	} else if Age >= 20 && Age <= 29 {
		InsertData.AgeRange = 29
	} else if Age >= 30 && Age <= 39 {
		InsertData.AgeRange = 39
	} else if Age >= 40 && Age <= 49 {
		InsertData.AgeRange = 49
	} else if Age >= 50 {
		InsertData.AgeRange = 50
	} else {
		InsertData.AgeRange = 0
	}
	InsertData.AddTime = time.Now()
	InsertData.CreatedDate = time.Now()
	db.Model(&models.MaPhysicalInfo{}).Create(&InsertData)
	return
}

// 检验机器是否存在
func (r RobotService) CheckRobotExist(ctx context.Context, db *gorm.DB, salesID, robotName string) (RobotInfo models.MaRobot, exist bool) {
	req := &devicec.DeviceDetailReq{}
	req.IdentificationCode = salesID
	if robotName != "" { // IdentificationCode RobotId 二选一即可，有就传
		req.RobotId = robotName
	}

	deviceClient, clientErr := client.GetDeviceAdminClient()
	if clientErr != nil {
		logx.Error(context.Background(), "grpc device client error", logx.Err(clientErr))
		return RobotInfo, false
	}
	res, err := deviceClient.DeviceDetail(context.Background(), req)
	if err != nil || res.Code == -1 {
		return RobotInfo, false
	}

	var RobotCount int64
	var dbRobotInfo models.MaRobot
	RobotCount = db.Model(&models.MaRobot{}).
		Where("robot_name = ? and deleted = 0", res.Device.RobotId).
		Take(&dbRobotInfo).RowsAffected
	// 执行新增
	if RobotCount == 0 {
		var InsertData models.MaRobot
		InsertData.ID = res.Device.MerchantId
		InsertData.MerchantID = res.Device.MerchantId
		InsertData.SalesID = res.Device.IdentificationCode
		InsertData.RobotType = strconv.Itoa(int(res.Device.DeviceType))
		InsertData.RobotName = res.Device.RobotId
		InsertData.AddTime = time.Now()

		// 机器辅助信息写入（机器的省市商场）
		InsertData.Areas1, _ = strconv.Atoi(res.Device.ProvinceId)
		InsertData.Areas2, _ = strconv.Atoi(res.Device.CityId)
		InsertData.Areas3, _ = strconv.Atoi(res.Device.LocationId)
		InsertData.Areas5, _ = strconv.Atoi(res.Device.DistrictId)
		InsertData.MerchantPath = res.Device.MerchantPath
		InsertData.CreatedAt = time.Now().Unix()
		db.Model(&models.MaRobot{}).Create(&InsertData)

	} else {
		// 如果更换了商户ID，则需要清空原有商户的支付方式
		if dbRobotInfo.ID != res.Device.MerchantId || dbRobotInfo.MerchantID != res.Device.MerchantId {
			// 执行更新
			UpdateData := make(map[string]interface{})
			UpdateData["id"] = res.Device.MerchantId
			UpdateData["merchant_id"] = res.Device.MerchantId
			UpdateData["merchant_path"] = res.Device.MerchantPath
			UpdateData["sales_id"] = res.Device.IdentificationCode
			UpdateData["payment_type1"] = 0
			UpdateData["payment_type2"] = 0
			UpdateData["payment_type3"] = 0
			UpdateData["payment_type4"] = 0
			UpdateData["payment1"] = 0
			UpdateData["payment2"] = 0
			UpdateData["payment3"] = 0
			UpdateData["payment4"] = 0
			db.Model(&models.MaRobot{}).
				Where("robot_name = ?", res.Device.RobotId).
				Updates(&UpdateData)
		}
		// 检测机器的商户ID
		// 检测机器的省市区商场
		// =========== 配置如果与GRPC获取不同，则执行更新
		myAreas1, _ := strconv.Atoi(res.Device.ProvinceId)
		myAreas2, _ := strconv.Atoi(res.Device.CityId)
		myAreas3, _ := strconv.Atoi(res.Device.LocationId)
		myAreas5, _ := strconv.Atoi(res.Device.DistrictId)
		if dbRobotInfo.Areas1 != myAreas1 || dbRobotInfo.Areas2 != myAreas2 || dbRobotInfo.Areas3 != myAreas3 || dbRobotInfo.Areas5 != myAreas5 {
			// 执行更新
			var UpdateData models.MaRobot
			UpdateData.Areas1 = myAreas1
			UpdateData.Areas2 = myAreas2
			UpdateData.Areas3 = myAreas3
			UpdateData.Areas5 = myAreas5

			db.Model(&models.MaRobot{}).
				Where("robot_name = ?", res.Device.RobotId).
				Select("areas1", "areas2", "areas3", "areas5").
				Updates(&UpdateData)
		}
	}
	db.Model(&models.MaRobot{}).
		Where("robot_name = ? and deleted = 0", res.Device.RobotId).
		Take(&RobotInfo)
	return RobotInfo, true
}

// Login 机器登录
func (r RobotService) Login(ctx context.Context, db *gorm.DB, salesID, robotName string) (data models.MaRobot, err error) {
	if salesID == "" || robotName == "" {
		err = errorx.New("参数不能为空", -1)
		return
	}
	// 1. 检测机器是否存在
	RobotInfo, robotExist := r.CheckRobotExist(ctx, db, salesID, robotName)
	if !robotExist {
		err = errorx.New("找不到机器人", -1)
		return
	}
	// 如果机器id或唯一识别码不符，返回false
	if RobotInfo.RobotName != robotName || RobotInfo.SalesID != salesID {
		err = errorx.New("找不到机器人", -1)
		return
	}

	// 2. 更新上报时间
	db.Model(&models.MaRobot{}).
		Where("sales_id = ? and deleted = 0", salesID).
		Updates(map[string]interface{}{
			"activation":         1,
			"last_reported_time": time.Now(),
		})
	return RobotInfo, nil
}

type ContainerInfo struct {
	Num          int  `json:"num" json_old:"Num"`
	Floor        int  `json:"floor" json_old:"Floor"`
	Inventory    uint `json:"inventory" json_old:"Inventory"`
	MaxInventory uint `json:"max_inverntory" json_old:"MaxInventory"`
}
type ResultGoodsInfoOld struct {
	SalesID            string                   `json:"sales_id" json_old:"SalesID"`
	GoodsID            int                      `json:"goods_id" json_old:"GoodsID"`
	GoodsInventory     int                      `json:"goods_inventory" json_old:"GoodsInventory"`
	GoodsMaxInventory  int                      `json:"goods_max_inventory" json_old:"GoodsMaxInventory"`
	ContainerFloor     int                      `json:"container_floor" json_old:"ContainerFloor"`
	ContainerNum       int                      `json:"container_num" json_old:"ContainerNum"`
	AddTime            string                   `json:"add_time" json_old:"AddTime"`
	ClassID            int                      `json:"class_id" json_old:"ClassID"`
	GoodsStatus        int                      `json:"goods_status" json_old:"GoodsStatus"`
	ContainerType      int                      `json:"container_type" json_old:"ContainerType"`
	Sort               int                      `json:"sort" json_old:"Sort"`
	ContainerStatus    int                      `json:"container_status" json_old:"ContainerStatus"`
	ContainerUseStatus int                      `json:"container_use_status" json_old:"ContainerUseStatus"`
	CreatedAt          int64                    `json:"created_at" json_old:"CreatedAt"`
	Price              float64                  `json:"price" json_old:"Price"`
	GoodsName1         string                   `json:"goods_name1" json_old:"GoodsName1"`
	GoodsDescription1  string                   `json:"goods_description1" json_old:"GoodsDescription1"`
	GoodsImage         string                   `json:"goods_image" json_old:"GoodsImage"`
	TotalSales         int                      `json:"total_sales" json_old:"TotalSales"`
	GoodsCode          int                      `json:"goods_code" json_old:"GoodsCode"`
	UseRC              uint                     `json:"use_rc" json_old:"UseRC"`
	GoodsName2         string                   `json:"goods_name2" json_old:"GoodsName2"`
	GoodsDescription2  string                   `json:"goods_description2" json_old:"GoodsDescription2"`
	GoodsName3         string                   `json:"goods_name3" json_old:"GoodsName3"`
	GoodsDescription3  string                   `json:"goods_description3" json_old:"GoodsDescription3"`
	CodeImage          string                   `json:"code_image" json_old:"CodeImage"`
	IsAuth             int                      `json:"is_auth" json_old:"IsAuth"`
	Price1             interface{}              `json:"price1" json_old:"Price1"`
	GoodsDetail        string                   `json:"goods_detail" json_old:"GoodsDetail"`
	GoodsVoice         string                   `json:"goods_voice" json_old:"GoodsVoice"`
	ShowTag            string                   `json:"show_tag" json_old:"ShowTag"`
	GoodsType          int                      `json:"goods_type" json_old:"GoodsType"`
	Spring             string                   `json:"spring" json_old:"Spring"`
	Container          []ContainerInfo          `json:"container" json_old:"Container"`
	Multi              int                      `json:"multi" json_old:"Multi"`
	MultiGroupID       int                      `json:"multi_group_id" json_old:"MultiGroupID"`
	MultiGroupName     string                   `json:"multi_group_name" json_old:"MultiGroupName"`
	MultiGroupDetail   string                   `json:"multi_group_detail" json_old:"MultiGroupDetail"`
	MultiSort          int                      `json:"multi_sort" json_old:"MultiSort"`
	MultiGroupImage    []string                 `json:"multi_group_image" json_old:"MultiGroupImage"`
	Activity           int                      `json:"activity" json_old:"Activity"`
	ActivityList       []MultiDiscountGoodsInfo `json:"activity_list" json_old:"ActivityList"`
	Points             float64                  `json:"points" json_old:"Points"`
	PointsSupport      int                      `json:"points_support" json_old:"PointsSupport"`
	VoiceKeywords      []string                 `json:"voice_keywords" json_old:"VoiceKeywords"`
	Recommend          int                      `json:"recommend" json_old:"Recommend"`
	RecommendImage     string                   `json:"recommend_image" json_old:"RecommendImage"`
	AdImage            string                   `json:"ad_image" json_old:"AdImage"`
	CornerLabelImage   string                   `json:"corner_label_image" json_old:"CornerLabelImage"`
	TagCode            string                   `json:"tag_code" json_old:"TagCode"`
	PurchaseType       int                      `json:"purchase_type" json_old:"PurchaseType"`
}
type ResultGoodsInfo struct {
	SalesID               string                   `json:"sales_id" json_old:"SalesID"`
	GoodsID               string                   `json:"goods_id" json_old:"GoodsID"`
	GoodsInventory        int                      `json:"goods_inventory" json_old:"GoodsInventory"`
	GoodsReserveInventory int                      `json:"goods_reserve_inventory" json_old:"GoodsReserveInventory"`
	GoodsMaxInventory     int                      `json:"goods_max_inventory" json_old:"GoodsMaxInventory"`
	ContainerFloor        int                      `json:"container_floor" json_old:"ContainerFloor"`
	ContainerNum          int                      `json:"container_num" json_old:"ContainerNum"`
	AddTime               string                   `json:"add_time" json_old:"AddTime"`
	ClassID               string                   `json:"class_id" json_old:"ClassID"`
	GoodsStatus           int                      `json:"goods_status" json_old:"GoodsStatus"`
	ContainerType         int                      `json:"container_type" json_old:"ContainerType"`
	Sort                  int                      `json:"sort" json_old:"Sort"`
	ContainerStatus       int                      `json:"container_status" json_old:"ContainerStatus"`
	ContainerUseStatus    int                      `json:"container_use_status" json_old:"ContainerUseStatus"`
	CreatedAt             int64                    `json:"created_at" json_old:"CreatedAt"`
	Price                 float64                  `json:"price" json_old:"Price"`
	GoodsName1            string                   `json:"goods_name1" json_old:"GoodsName1"`
	GoodsDescription1     string                   `json:"goods_description1" json_old:"GoodsDescription1"`
	GoodsImage            string                   `json:"goods_image" json_old:"GoodsImage"`
	TotalSales            int                      `json:"total_sales" json_old:"TotalSales"`
	GoodsCode             string                   `json:"goods_code" json_old:"GoodsCode"`
	UseRC                 uint                     `json:"use_rc" json_old:"UseRC"`
	GoodsName2            string                   `json:"goods_name2" json_old:"GoodsName2"`
	GoodsDescription2     string                   `json:"goods_description2" json_old:"GoodsDescription2"`
	GoodsName3            string                   `json:"goods_name3" json_old:"GoodsName3"`
	GoodsDescription3     string                   `json:"goods_description3" json_old:"GoodsDescription3"`
	CodeImage             string                   `json:"code_image" json_old:"CodeImage"`
	IsAuth                int                      `json:"is_auth" json_old:"IsAuth"`
	Price1                float64                  `json:"price1" json_old:"Price1"`
	GoodsDetail           string                   `json:"goods_detail" json_old:"GoodsDetail"`
	GoodsVoice            string                   `json:"goods_voice" json_old:"GoodsVoice"`
	ShowTag               string                   `json:"show_tag" json_old:"ShowTag"`
	GoodsType             int                      `json:"goods_type" json_old:"GoodsType"`
	Spring                string                   `json:"spring" json_old:"Spring"`
	Container             []ContainerInfo          `json:"container" json_old:"Container"`
	Multi                 int                      `json:"multi" json_old:"Multi"`
	MultiGroupID          string                   `json:"multi_group_id" json_old:"MultiGroupID"`
	MultiGroupName        string                   `json:"multi_group_name" json_old:"MultiGroupName"`
	MultiGroupDetail      string                   `json:"multi_group_detail" json_old:"MultiGroupDetail"`
	MultiSort             int                      `json:"multi_sort" json_old:"MultiSort"`
	MultiGroupImage       []string                 `json:"multi_group_image" json_old:"MultiGroupImage"`
	Activity              int                      `json:"activity" json_old:"Activity"`
	ActivityList          []MultiDiscountGoodsInfo `json:"activity_list" json_old:"ActivityList"`
	Points                float64                  `json:"points" json_old:"Points"`
	PointsSupport         int                      `json:"points_support" json_old:"PointsSupport"`
	VoiceKeywords         string                   `json:"voice_keywords" json_old:"VoiceKeywords"`
	Recommend             int                      `json:"recommend" json_old:"Recommend"`
	RecommendImage        string                   `json:"recommend_image" json_old:"RecommendImage"`
	AdImage               string                   `json:"ad_image" json_old:"AdImage"`
	CornerLabelImage      string                   `json:"corner_label_image" json_old:"CornerLabelImage"`
	Merchant              string                   `json:"merchant" json_old:"merchant"`
	TagCode               string                   `json:"tag_code" json_old:"TagCode"`
	PurchaseType          int                      `json:"purchase_type" json_old:"PurchaseType"`
}

type MultiDiscountGoodsInfo struct {
	GoodsID       string `json:"goods_id" json_old:"GoodsID"`
	Discount      string `json:"discount" json_old:"Discount"`
	Price         string `json:"price" json_old:"Price"`
	DiscountPrice string `json:"discount_price" json_old:"DiscountPrice"`
	ExchangePrice string `json:"exchange_price" json_old:"ExchangePrice"`
}

// UpdateToOrderTimes 上报出货时间 [机器 + 订单号]
func (r RobotService) UpdateToOrderTimes(ctx context.Context, db *gorm.DB, merchantID, salesID, OutTradeNo string,
	times struct {
		Time1  int `json:"time1"`
		Time2  int `json:"time2"`
		Time3  int `json:"time3"`
		Time4  int `json:"time4"`
		Time5  int `json:"time5"`
		Time6  int `json:"time6"`
		Time7  int `json:"time7"`
		Time8  int `json:"time8"`
		Time9  int `json:"time9"`
		Time10 int `json:"time10"`
	},
) (err error) {
	var OrderInfo models.MaOrder
	db.Model(&models.MaOrder{}).
		Where("sales_id = ? and out_trade_no = ?", salesID, OutTradeNo).
		Take(&OrderInfo)
	if OrderInfo.MerchantID == "" {
		err = errorx.New("上报失败", -1)
		return
	}
	timesbyte, _ := json.Marshal(times)
	err = db.Model(&models.MaOrderDetails{}).
		Where("id = ?", OrderInfo.OrderID).
		UpdateColumn("times", string(timesbyte)).Error
	if err != nil {
		err = errorx.New("上报失败", -1)
	}
	return
}

type MicropayStruct struct {
	Data struct {
		AliPayResult string `json:"ali_pay_result" json_old:"AliPayResult"`
	} `json:"data" json_old:"Data"`
}

// Micropay 刷脸支付
func (r RobotService) Micropay(ctx context.Context, db *gorm.DB, salesID, OutTradeNo, TradeNo,
	AuthCode string, PayType int, PaymentAmount float64,
) (res MicropayStruct, err error) {
	// 判断订单是否已经支付
	{
		order := models.MaOrder{}
		mysql.NewDB().Model(&models.MaOrder{}).Where("out_trade_no = ?", TradeNo).Take(&order)
		if order.OrderStatus == 2 {
			err = errorx.New("订单已支付", -1)
			return
		} else {
			// 新增1:人脸支付，先把确认订单号更新进去（作用是：后面方便自动查询时可以退款）
			var updateData = make(map[string]interface{})
			updateData["end_out_trade_no"] = OutTradeNo
			mysql.NewDB().Model(&models.MaOrder{}).
				Where("out_trade_no = ?", TradeNo).
				Updates(&updateData)
		}
	}
	RobotInfo, robotExist := r.CheckRobotExist(ctx, db, salesID, "")
	if !robotExist {
		err = errorx.New("找不到机器人", -1)
		return
	}

	// 商户信息
	MerchantInfo, count, _ := dao.MerchantConfig{}.GetDetail(ctx, db, RobotInfo.MerchantID)
	if count == 0 {
		err = errorx.New("商户信息不存在", -1)
		return
	}

	// 选择支付方式，获取商户的支付方式及支付配置
	PaymentTypeMap := service.RobotPayment{}.GetRobotPayments(context.Background(), db, salesID, 0)
	if _, ok := PaymentTypeMap["4"]; ok { // 4 支付宝人脸
		AliPayConfig, _ := dao.Payment{}.GetAliPay(context.Background(), dal.Q, MerchantInfo.ID, PaymentTypeMap["4"])
		if len(AliPayConfig) == 0 {
			err = errorx.New("支付宝没有配置具体的支付参数", -1)
			return
		}
		err1 := pay.Alipay{}.ConfigInit(AliPayConfig[0].AliPayAppID, AliPayConfig[0].AliPayPrivateKey)
		if err1 != nil {
			return
		}
		var scene string
		if PayType == 1 {
			scene = "bar_code" // 条码支付
		} else if PayType == 2 {
			scene = "security_code" // 刷脸支付
		}
		alipayRes, err2 := pay.Alipay{}.AliPayMicropay(OutTradeNo, AuthCode, scene, PaymentAmount)
		if err2 != nil {
			res.Data.AliPayResult = "Error"
			err = errorx.New("刷脸支付失败", -1)
			return
		}
		if alipayRes.Response.Code == "10000" {
			res.Data.AliPayResult = alipayRes.Response.Msg
			// 更新订单状态
			updateStatus := order.OrderService{}.UpdateOrderPaymentSuccessful(ctx, db, TradeNo, OutTradeNo, "TRADE_SUCCESS", "4")
			if !updateStatus {
				err = errorx.New("人脸支付成功，状态更改失败", -1)
				return
			}
		} else if alipayRes.Response.Code == "10003" {
			res.Data.AliPayResult = "GoQuery"
		} else {
			res.Data.AliPayResult = "Error"
		}
		return res, nil
	}
	return
}

// MicropayInit 刷脸支付初始化
type MicroPayStruct struct {
	ZimID             string `json:"zim_id" json_old:"zim_id"`
	ZimInitClientData string `json:"zim_init_client_data" json_old:"zim_init_client_data"`
}

func (r RobotService) MicropayInit(ctx context.Context, db *gorm.DB, merchantID, salesID string, MetaInfo interface{}) (Rsp MicroPayStruct, err error) {
	RobotInfo, robotExist := r.CheckRobotExist(ctx, db, salesID, "")
	if !robotExist {
		err = errorx.New("找不到机器人", -1)
		return
	}

	// 商户信息
	MerchantInfo, count, _ := dao.MerchantConfig{}.GetDetail(ctx, db, RobotInfo.ID)
	if count == 0 {
		err = errorx.New("商户信息不存在", -1)
		return
	}

	// 选择支付方式，获取商户的支付方式及支付配置
	PaymentTypeMap := service.RobotPayment{}.GetRobotPayments(context.Background(), db, salesID, 0)
	if _, ok := PaymentTypeMap["4"]; ok { // 4 支付宝人脸
		AliPayConfig, _ := dao.Payment{}.GetAliPay(context.Background(), dal.Q, MerchantInfo.ID, PaymentTypeMap["4"])
		if len(AliPayConfig) == 0 {
			err = errorx.New("支付宝没有配置具体的支付参数", -1)
			return
		}
		err1 := pay.Alipay{}.ConfigInit(AliPayConfig[0].AliPayAppID, AliPayConfig[0].AliPayPrivateKey)
		if err1 != nil {
			return
		}
		alipayRes, err2 := pay.Alipay{}.AliPayMicropayInit(MetaInfo)
		if err2 != nil {
			err = errorx.New("人脸支付初始化错误", -1)
			return
		}
		if alipayRes.Response.Code == "10000" {
			Rsp.ZimID = alipayRes.Response.ZimId
			Rsp.ZimInitClientData = alipayRes.Response.ZimInitClientData
		}
		return Rsp, nil
	}
	return
}

type QueryOrderDataStruct struct {
	Type string `json:"type" json_old:"Type"`
	Msg  string `json:"msg" json_old:"Msg"`
}
type QueryOrderStruct struct {
	Data       []QueryOrderDataStruct `json:"data" json_old:"Data"`
	PaySuccess int                    `json:"pay_success" json_old:"PaySuccess"`
	Result     struct {
		WechatPayResult wechat.QueryOrderResponse `json:"wechat_pay_result" json_old:"WechatPayResult"`
		AliPayResult    alipay.TradeQueryResponse `json:"ali_pay_result" json_old:"AliPayResult"`
	} `json:"result" json_old:"Result"`
}

// QueryOrder 查询订单
func (r RobotService) QueryOrder(ctx context.Context, salesID, outTradeNo string) (
	Result QueryOrderStruct,
	err error,
) {
	logx.Info(ctx, "QueryOrder:"+outTradeNo, logx.Any("type", "query_order"), logx.Any("sales_id", salesID))
	Result = QueryOrderStruct{}
	db := mysql.NewDB()
	// 查询订单信息
	var OrderInfo models.MaOrder
	var OrderInfoDetail models.MaOrderDetails
	var OrderCount int64

	// 查询订单主表 MaOrder
	db.Model(&models.MaOrder{}).
		Where("out_trade_no = ?", outTradeNo).
		Take(&OrderInfo).Count(&OrderCount)

	logx.Info(ctx, "QueryOrder", logx.Any("OrderInfo.PaymentID ", OrderInfo.PaymentID), logx.Any("outTradeNo", outTradeNo))
	// 查询订单辅表 MaOrderDetail
	db.Model(&models.MaOrderDetails{}).Where("id = ?", OrderInfo.OrderID).Take(&OrderInfoDetail)

	if OrderCount == 0 {
		logx.Info(ctx, "QueryOrder", logx.Any("Err", "订单信息不存在"))
		err = errorx.New("订单信息不存在", -1)
		return
	}

	// 查询订单详情表获取用户选择的预留支付方式。如果存在，则只查询当前的支付方式

	RobotInfo, robotExist := r.CheckRobotExist(ctx, db, salesID, "")
	logx.Info(ctx, "QueryOrder", logx.Any("RobotInfo.ID", RobotInfo.ID), logx.Any("outTradeNo", outTradeNo))
	if !robotExist {
		logx.Info(ctx, "QueryOrder", logx.Any("Err", "找不到机器人"))
		err = errorx.New("找不到机器人", -1)
		return
	}

	MerchantInfo, count, _ := dao.MerchantConfig{}.GetDetail(ctx, db, RobotInfo.ID)
	if count == 0 {
		logx.Info(ctx, "QueryOrder", logx.Any("Err", "商户信息不存在"))
		err = errorx.New("商户信息不存在", -1)
		return
	}

	// 0元购
	if OrderInfo.PaymentID == 15 {
		if OrderInfo.OrderStatus == 2 {
			Result.Data = append(Result.Data, QueryOrderDataStruct{
				Type: "15", // 0元购
			})
			Result.PaySuccess = 1
		}
		return
	}

	// 检测给机器分配的支付方式是否被使用过
	// 选择支付方式，获取商户的支付方式及支付配置
	// 获取当前机器的支付方式的值【集合】
	PaymentTypeMap := service.RobotPayment{}.GetRobotPayments(context.Background(), db, salesID, OrderInfoDetail.PaymentID)

	// 积分系统
	if OrderInfo.PaymentID == 9 {
		if _, ok := PaymentTypeMap["9"]; ok { // 9 积分系统 微信支付
			if OrderInfo.ActualAmount != 0 { // 实际支付金额
				// 微信查询订单
				pay.WechatPay{}.V3ConfigInit("", "", "")
				wechatPayRsp, wechatPayErr := pay.WechatPay{}.V3TransactionQueryOrder(ctx, outTradeNo+"P")
				if wechatPayErr == nil && wechatPayRsp.Code == 0 && wechatPayRsp.Response.TradeState == "SUCCESS" {
					// 更改订单状态
					updateRes := order.OrderService{}.UpdateOrderPaymentSuccessful(ctx, db, outTradeNo, outTradeNo+"P", "TRADE_SUCCESS", "9") // 9 积分支付
					if !updateRes {
						err = errorx.New("用户支付成功，状态修改失败", -1)
						return
					}
					Result.PaySuccess = 1
					OrderInfo.PaymentID = 9
				}
				logx.Error(ctx, "积分路过记录，临时测试0",
					logx.Any("out_trade_no", outTradeNo+"P"),
					logx.Any("wechatPayErr", wechatPayErr),
					logx.Any("wechatPayRsp Code", wechatPayRsp.Code),
					logx.Any("wechatPayRsp TradeState", wechatPayRsp.Response.TradeState))
				//取消微信支付，积分冲正
				if wechatPayErr == nil && wechatPayRsp.Code == 0 && wechatPayRsp.Response.TradeState == "CLOSED" {
					OrderPointsInfo, _ := dao.OrderPoints{}.Detail(ctx, db, OrderInfo.OutTradeNo, 1)
					if OrderPointsInfo != nil && OrderPointsInfo.OutTradeNo == outTradeNo {
						orderRefundNo, _ := helper.UUID{}.String()
						RobotLocationInfo, _ := service.OrderPoints{}.GetRobotLocationInfo(ctx, db, OrderPointsInfo.SalesID)
						tradeErr := points.NewPoints(OrderPointsInfo.PointsID).PointsReverse(
							ctx,
							db,
							OrderPointsInfo.SalesID,        // 1. 机器识别码
							OrderPointsInfo.OpenID,         // 2. OpenID
							orderRefundNo,                  // 3. 退款单号
							OrderPointsInfo.OutTradeNo,     // 4. 订单号
							-OrderPointsInfo.Points,        // 5. 需要退款的金额（正数）
							RobotLocationInfo.PointsMallId, // 6. 积分对接的商场ID
						)
						logx.Error(ctx, "积分路过记录，临时测试1",
							logx.Any("out_trade_no", outTradeNo+"P"),
							logx.Any("points", -OrderPointsInfo.Points),
							logx.Any("error", tradeErr),
						)
						if tradeErr != nil {
							logx.Error(ctx, "正弘积分取消支付错误", logx.Any("out_trade_no", outTradeNo+"P"), logx.Any("error", tradeErr))
						} else {
							// 订单积分冲正记录
							dao.OrderPoints{}.Create(ctx, db, OrderPointsInfo.SalesID, OrderPointsInfo.OpenID, OrderPointsInfo.OutTradeNo, 2, OrderPointsInfo.PointsID, -OrderPointsInfo.Points)
						}
					} else {
						logx.Error(ctx, "积分路过记录，临时测试3", logx.Any("out_trade_no", outTradeNo+"P"))
					}
				} else {
					logx.Error(ctx, "积分路过记录，临时测试4", logx.Any("out_trade_no", outTradeNo+"P"))
				}

			} else {
				// 没有实付金额时查积分消费
				OrderPointsInfo, _ := dao.OrderPoints{}.Detail(ctx, db, outTradeNo, 0)
				if OrderPointsInfo != nil && OrderPointsInfo.Type == 1 {
					Result.PaySuccess = 1
				}
			}
			return
		}
	}

	if OrderInfo.PaymentID == 17 {
		Result.PaySuccess = 1
		Result.Data = append(Result.Data, QueryOrderDataStruct{
			Type: "17", // 小程序支付
			Msg:  "1",
		})
		return
	}

	// 没有配置
	if len(PaymentTypeMap) == 0 {

		logx.Info(ctx, "QueryOrder", logx.Any("Err", "没有设置支付方式"))
		// 获取商户默认支付配置
		PaymentTypeMap = service.RobotPayment{}.GetRobotDefaultPayments(context.Background(), db, salesID, MerchantInfo.ID, OrderInfoDetail.PaymentID)
		if len(PaymentTypeMap) == 0 {
			logx.Info(ctx, "QueryOrder", logx.Any("Err", "没有设置支付方式2"))
			err = errorx.New("没有设置支付方式", -1)
			return
		}
	}

	for k, v := range PaymentTypeMap {

		logx.Info(ctx, "QueryOrder", logx.Any("PaymentTypeMap", k))
		if k == "1" { // 1 微信
			WechatPayConfig, _ := dao.Payment{}.GetWechatPay(context.Background(), dal.Q, "", v)
			if len(WechatPayConfig) == 0 {
				err = errorx.New("微信没有配置具体的支付参数", -1)
				return
			}
			// 微信查询订单
			err = pay.WechatPay{}.ConfigInit(WechatPayConfig[0].WeChatAppID, WechatPayConfig[0].WeChatMchID,
				WechatPayConfig[0].WeChatAppKey, WechatPayConfig[0].CertFile)
			if err != nil {
				return
			}
			wechatPayRsp, wechatPayErr := pay.WechatPay{}.QueryOrder(outTradeNo)
			if wechatPayErr == nil && wechatPayRsp.ResultCode == "SUCCESS" && wechatPayRsp.TradeState == "SUCCESS" {
				if OrderInfo.PaymentID == 0 || OrderInfo.PaymentID == 1 { // 微信支付
					// 更改订单状态
					updateRes := order.OrderService{}.UpdateOrderPaymentSuccessful(ctx, db, outTradeNo, outTradeNo, "TRADE_SUCCESS", "1") // 1 微信
					if !updateRes {
						err = errorx.New("用户支付成功，状态修改失败", -1)
						return
					}
					Result.Data = append(Result.Data, QueryOrderDataStruct{
						Type: "1", // 微信支付
						Msg:  "1",
					})
					Result.PaySuccess = 1
					OrderInfo.PaymentID = 1
				}
			}
			Result.Data = append(Result.Data, QueryOrderDataStruct{
				Type: "1",
			})
			Result.Result.WechatPayResult = wechatPayRsp
		} else if k == "2" { // 2 支付宝
			AliPayConfig, _ := dao.Payment{}.GetAliPay(context.Background(), dal.Q, "", v)
			if len(AliPayConfig) == 0 {
				err = errorx.New("支付宝没有配置具体的支付参数", -1)
				return
			}
			err1 := pay.Alipay{}.ConfigInit(AliPayConfig[0].AliPayAppID, AliPayConfig[0].AliPayPrivateKey)
			if err1 != nil {
				err = errorx.New("支付宝配置错误", -1)
				return
			}
			// 查询支付宝订单
			alipayRsp, alipayErr := pay.Alipay{}.AliPayQuery(outTradeNo)
			if alipayErr == nil && alipayRsp.Response != nil && alipayRsp.Response.Code == "10000" && alipayRsp.Response.TradeStatus == "TRADE_SUCCESS" {
				if OrderInfo.PaymentID == 0 || OrderInfo.PaymentID == 2 { // 支付宝支付
					// 更改订单状态
					updateRes := order.OrderService{}.UpdateOrderPaymentSuccessful(ctx, db, outTradeNo, outTradeNo, "TRADE_SUCCESS", "2") // 2 支付宝
					if !updateRes {
						err = errorx.New("用户支付成功，状态修改失败", -1)
						return
					}
					Result.Data = append(Result.Data, QueryOrderDataStruct{
						Type: "2", // 支付宝支付
						Msg:  "1",
					})
					Result.PaySuccess = 1
					OrderInfo.PaymentID = 2
				}
			}
			Result.Data = append(Result.Data, QueryOrderDataStruct{
				Type: "2",
			})
			Result.Result.AliPayResult = alipayRsp
		} else if k == "6" { // 6 大兴支付
			DaxingConfig, _ := dao.Payment{}.GetDaxingPay(context.Background(), dal.Q, "", v)
			if len(DaxingConfig) == 0 {
				err = errorx.New("没有配置支付参数", -1)
				return
			}
			CountData := common.Order{}.CountDataOldToNew(OrderInfo.CountData)
			json.Unmarshal([]byte(OrderInfo.CountData), &CountData)

			var GoodsInfo models.MaGoodsInfo
			db.Model(&models.MaGoodsInfo{}).
				Where("goods_id = ?", CountData[0].GoodsID).
				Take(&GoodsInfo)

			pay.DaxingPay{}.DaxingInit(DaxingConfig[0].DxAppID, DaxingConfig[0].DxSellerID, DaxingConfig[0].DxMerchantID,
				DaxingConfig[0].DxShopID, DaxingConfig[0].DxShopID, DaxingConfig[0].DxAppKey)

			// 查询微信
			daxingWxRsp, daxingWxErr := pay.DaxingPay{}.QueryOrder(ctx, "WX", outTradeNo+"A", OrderInfo.PaymentAmount)
			if daxingWxErr != nil {
				err = errorx.New("大兴支付查询失败", -1)
				return
			}
			var WeChatRequest string
			if daxingWxRsp.Result.OutID != "" {
				if daxingWxRsp.Result.Status == "30" {
					WeChatRequest = "SUCCESS"
				} else {
					WeChatRequest = "NOTPAY"
				}
			} else {
				WeChatRequest = "NOTPAY"
			}
			Result.Data = append(Result.Data, QueryOrderDataStruct{
				Type: "3", // 大兴 3 微信
				Msg:  WeChatRequest,
			})
			// 查询支付宝
			daxingAliRsp, daxingAliErr := pay.DaxingPay{}.QueryOrder(ctx, "ALI_20", outTradeNo+"A", OrderInfo.PaymentAmount)
			if daxingAliErr != nil {
				err = errorx.New("大兴支付查询失败", -1)
				return
			}
			var AliRequest string
			if daxingAliRsp.Result.OutID != "" {
				if daxingAliRsp.Result.Status == "30" {
					AliRequest = "TRADE_SUCCESS"
				} else {
					AliRequest = "WAIT_BUYER_PAY"
				}
			} else {
				AliRequest = "WAIT_BUYER_PAY"
			}
			daxingWxRspBytes, _ := json.Marshal(daxingWxRsp)
			daxingAliRspBytes, _ := json.Marshal(daxingAliRsp)
			// 更新订单
			db.Model(&models.MaOrderDetails{}).
				Where("id = ?", OrderInfo.OrderID).
				Updates(map[string]interface{}{
					"wechat_query_result": string(daxingWxRspBytes),
					"alipay_query_result": string(daxingAliRspBytes),
					"modify_time":         time.Now(),
				})
			var GoodsNum int
			if OrderInfo.PaymentAmount > 0 {
				GoodsNum = 1
			} else {
				GoodsNum = -1
			}
			if WeChatRequest == "SUCCESS" {
				updateRes := order.OrderService{}.UpdateOrderPaymentSuccessful(ctx, db, outTradeNo, outTradeNo+"W", "SUCCESS", "6") // 6 大兴
				if !updateRes {
					err = errorx.New("用户微信支付成功，状态更改失败", -1)
					return
				}
				// 向大兴发送销售数据，并生成订单
				pay.DaxingPay{}.UpSales(ctx, outTradeNo, GoodsNum, OrderInfo.PaymentAmount, GoodsInfo.GoodsCode, "WX")
				Result.Data = append(Result.Data, QueryOrderDataStruct{
					Type: "3",
					Msg:  "1",
				})
				Result.PaySuccess = 1
				return
			}
			Result.Data = append(Result.Data, QueryOrderDataStruct{
				Type: "4", // 大兴 4 支付宝
				Msg:  AliRequest,
			})
			if AliRequest == "TRADE_SUCCESS" {
				updateRes := order.OrderService{}.UpdateOrderPaymentSuccessful(ctx, db, outTradeNo, outTradeNo+"A", "TRADE_SUCCESS", "6") // 6 大兴
				if !updateRes {
					err = errorx.New("用户支付宝支付成功，状态更改失败", -1)
					return
				}
				// 向大兴发送销售数据，并生成订单
				pay.DaxingPay{}.UpSales(ctx, outTradeNo, GoodsNum, OrderInfo.PaymentAmount, GoodsInfo.GoodsCode, "ALI_20")
				Result.Data = append(Result.Data, QueryOrderDataStruct{
					Type: "4",
					Msg:  "1",
				})
				Result.PaySuccess = 1
				return
			}
		} else if k == "8" || k == "10" { // 8 stripe支付  10 stripe_ex支付
			db := mysql.NewDB()
			var orderInfo models.MaOrder
			db.Model(&models.MaOrder{}).
				Where("out_trade_no = ?", outTradeNo).
				Take(&orderInfo)
			if orderInfo.OrderStatus == 2 {
				Result.PaySuccess = 1
			}
		} else if k == "12" { // 12 小天才
			WechatPayConfig, _ := dao.Payment{}.GetXiaotiancaiPay(context.Background(), dal.Q, "", v)
			if len(WechatPayConfig) == 0 {
				err = errorx.New("小天才没有配置具体的支付参数", -1)
				return
			}
			// 微信查询订单
			err = pay.WechatPay{}.ConfigInit(WechatPayConfig[0].WeChatAppID, WechatPayConfig[0].WeChatMchID,
				WechatPayConfig[0].WeChatAppKey, WechatPayConfig[0].CertFile)
			if err != nil {
				return
			}
			wechatPayRsp, wechatPayErr := pay.WechatPay{}.QueryOrder(outTradeNo)
			if wechatPayErr == nil && wechatPayRsp.ResultCode == "SUCCESS" && wechatPayRsp.TradeState == "SUCCESS" {
				if OrderInfo.PaymentID == 0 || OrderInfo.PaymentID == 12 { // 小天才
					// 更改订单状态
					updateRes := order.OrderService{}.UpdateOrderPaymentSuccessful(ctx, db, outTradeNo, outTradeNo, "TRADE_SUCCESS", "12") // 12 小天才
					if !updateRes {
						err = errorx.New("用户支付成功，状态修改失败", -1)
						return
					}
					Result.Data = append(Result.Data, QueryOrderDataStruct{
						Type: "12", // 小天才
						Msg:  "1",
					})
					Result.PaySuccess = 1
					OrderInfo.PaymentID = 1
				}
			}
			Result.Data = append(Result.Data, QueryOrderDataStruct{
				Type: "1",
			})
			Result.Result.WechatPayResult = wechatPayRsp
		} else if k == "16" { // 16 小程序支付
			pay.WechatPay{}.V3ConfigInit("", "", "")
			wechatPayRsp, wechatPayErr := pay.WechatPay{}.V3TransactionQueryOrder(ctx, outTradeNo+"P")
			if wechatPayErr == nil && wechatPayRsp.Code == 0 && wechatPayRsp.Response.TradeState == "SUCCESS" {
				// 更改订单状态
				updateRes := order.OrderService{}.UpdateOrderPaymentSuccessful(ctx, db, outTradeNo, outTradeNo+"P", "TRADE_SUCCESS", "16") // 16 小程序支付
				if !updateRes {
					logx.Info(ctx, "QueryOrder", logx.Any("Err", "用户支付成功，状态修改失败"))
					err = errorx.New("用户支付成功，状态修改失败", -1)
					return
				}
				Result.Data = append(Result.Data, QueryOrderDataStruct{
					Type: "16", // 小程序支付
					Msg:  "1",
				})
				Result.PaySuccess = 1
				OrderInfo.PaymentID = 16
			}
		}
	}
	return Result, nil
}

// QueryRCOrder 兑换码订单查询
func (r RobotService) QueryRCOrder(ctx context.Context, salesID, outTradeNo, code string) (data interface{}, err error) {
	db := mysql.NewDB()
	RobotInfo, robotExist := r.CheckRobotExist(ctx, db, salesID, "")
	if !robotExist {
		err = errorx.New("找不到机器人", -1)
		return
	}
	var OrderInfo models.MaOrder
	db.Model(&models.MaOrder{}).
		Where("out_trade_no = ?", outTradeNo).
		Take(&OrderInfo)
	if OrderInfo.MerchantID == "" {
		err = errorx.New("订单信息不存在", -1)
		return
	}
	var CodeInfo models.MaRedemptionCode
	if RobotInfo.ID == "1" {
		db.Model(&models.MaRedemptionCode{}).
			Where("out_trade_no = ?", outTradeNo).
			Take(&CodeInfo)
		if CodeInfo.RedemptionCode != "" {
			err = errorx.New("该订单已被其他兑换码兑换，请重新创建订单", -1)
			return
		}
		db.Model(&models.MaRedemptionCode{}).
			Where("redemption_code = ?", code).
			Take(&CodeInfo)
	} else {
		db.Model(&models.MaRedemptionCode{}).
			Where("redemption_code = ?", code).
			Take(&CodeInfo)
	}
	if CodeInfo.RedemptionCode == "" {
		err = errorx.New("兑换码不匹配", 2)
		return
	} else if CodeInfo.Value < OrderInfo.PaymentAmount {
		err = errorx.New("兑换码金额不匹配", 4)
		return
	} else if CodeInfo.RedemptionState == 0 {
		err = errorx.New("兑换码已过期", -1)
		return
	} else if CodeInfo.RedemptionState == 2 {
		err = errorx.New("兑换码已使用", 6)
		return
	} else {
		// 创建兑换码订单
		db.Model(&models.MaRedemptionCode{}).
			Where("redemption_code", code).
			Updates(map[string]interface{}{
				"exchange_value":   OrderInfo.PaymentAmount,
				"sales_id":         salesID,
				"robot_name":       RobotInfo.RobotName,
				"usage_time":       time.Now(),
				"out_trade_no":     outTradeNo,
				"redemption_state": 2, // 已使用
				"new_usage_time":   time.Now().Unix(),
			})
	}
	return
}

type CloseOrderERsp struct {
	WechatPayResult wechat.CloseOrderResponse `json:"wechat_pay_result" json_old:"WechatPayResult"`
	AliPayResult    alipay.TradeCloseResponse `json:"ali_pay_result" json_old:"AliPayResult"`
}

type MyOrderStruct struct {
	OrderID            string  `json:"order_id"`
	MerchantID         string  `json:"merchant_id"`
	SalesID            string  `json:"sales_id"`
	RobotName          string  `json:"robot_name"`
	OutTradeNo         string  `json:"out_trade_no"`
	ChildOutTradeNo    string  `json:"child_out_trade_no"`
	RefundTradeNo      string  `json:"refund_trade_no"`
	OrderStatus        int     `json:"order_status"`
	End                int     `json:"end"`
	PaymentID          int8    `json:"payment_id"`
	Areas1             int     `json:"areas1"`
	Areas2             int     `json:"areas2"`
	Areas3             int     `json:"areas3"`
	Areas4             int     `json:"areas4"`
	Areas5             int64   `json:"areas5"`
	EndOutTradeNo      string  `json:"end_out_trade_no"`
	CountData          string  `json:"count_data"`
	PaymentAmount      float64 `json:"payment_amount"`
	TotalPaymentAmount float64 `json:"total_payment_amount"`
	DiscountAmount     float64 `json:"discount_amount"`
	ActualAmount       float64 `json:"actual_amount"`
	ContainerNumList   string  `json:"container_num_list"`
	Remarks            string  `json:"remarks"`
	ErrorCode          string  `json:"error_code"`
	LogMsg             string  `json:"log_msg"`
	OrigialOutTradeNo  string  `json:"origial_out_trade_no"`
}

// CloseLotteryOrder 抽奖订单关闭
func (r RobotService) CloseLotteryOrder(
	ctx context.Context,
	salesID, outTradeNo string,
	endType string,
	popUp int,
	errorCode string,
	endRemarks,
	origialOutTradeNo,
	logMsg,
	pushData string,
) (
	Response CloseOrderERsp,
	err error,
) {

	db := mysql.NewDB()

	// 检查并获取机器信息
	RobotInfo, robotExist := r.CheckRobotExist(ctx, db, salesID, "")
	if !robotExist {
		err = errorx.New("找不到机器人", -1)
		return
	}
	// 获取抽奖订单信息
	var OrderInfo MyOrderStruct
	found := db.Model(&models.MaLotteryOrder{}).
		Where("out_trade_no = ?", outTradeNo).
		Select("*,sum(payment_amount) as total_payment_amount").
		Take(&OrderInfo).RowsAffected
	if found <= 0 {
		err = errorx.New("订单信息不存在", -1)
		return
	}

	// 关闭正常抽奖订单
	err2 := order.OrderService{}.CloseLotteryOrder(
		ctx,
		db,
		RobotInfo.ID,
		salesID,
		outTradeNo,
		endType,
		popUp,
		errorCode,
		endRemarks,
		logMsg,
		pushData,
	)
	if err2 != nil {
		logx.Error(context.Background(), "关闭订单失败", logx.String("outTradeNo", outTradeNo))
	}

	return
}

// CloseOrder 订单关闭
func (r RobotService) CloseOrder(ctx context.Context, salesID, outTradeNo string, endType string, popUp int, errorCode string,
	endRemarks, origialOutTradeNo, logMsg, pushData string,
) (Response CloseOrderERsp, err error) {
	db := mysql.NewDB()
	// 检查机器是否存在
	RobotInfo, robotExist := r.CheckRobotExist(ctx, db, salesID, "")
	if !robotExist {
		err = errorx.New("找不到机器人", -1)
		return
	}
	// 获取订单信息 ---- 判断是否是加购订单 （如果相同订单号 >=2 时，则认定为加购订单。反之为普通一笔订单）
	var OrderInfo MyOrderStruct
	db.Model(&models.MaOrder{}).
		Where("out_trade_no = ?", outTradeNo).
		Select("*,sum(payment_amount) as total_payment_amount").
		Take(&OrderInfo)
	if OrderInfo.OutTradeNo == "" {
		err = errorx.New("订单信息不存在", -1)
		return
	}
	// 出货失败，兑换码恢复
	if endType == "2" && OrderInfo.PaymentID == 5 {
		// 清空兑换码信息
		clearCode := db.Model(&models.MaRedemptionCode{}).
			Where("out_trade_no = ?", outTradeNo).
			Updates(map[string]interface{}{
				"exchange_value":   0,
				"sales_id":         "",
				"robot_name":       "",
				"usage_time":       0,
				"new_usage_time":   0,
				"out_trade_no":     "",
				"class_id":         "",
				"goods_id":         "",
				"redemption_state": 1,
			}).RowsAffected
		if clearCode == 0 {
			coupon.CouponCode{}.Refund(ctx, outTradeNo)
		}
	}
	// 获取机器已配置的支付方式
	PaymentTypeMap := service.RobotPayment{}.GetRobotPayments(context.Background(), db, salesID, 0)
	logx.Info(ctx, "close order debug", logx.Any("orderinfo", OrderInfo), logx.Any("PaymentTypeMap", PaymentTypeMap))

	// 向第三方发起退款
	if OrderInfo.PaymentID == 9 {
		// 积分系统 微信支付
		if _, ok := PaymentTypeMap["9"]; ok {
			// 订单结束[end_type=2]实际已支付 执行自动退款
			if endType == "2" { // 0出货失败 1出货成功 2订单结束(自己取消)
				if OrderInfo.ActualAmount > 0 { // 实付金额大于0时 判断微信退款
					err1 := pay.WechatPay{}.V3ConfigInit("", "", "")
					if err1 != nil {
						logx.Error(context.Background(), "WechatPay V3ConfigInit error", logx.Err(err1))
					} else {
						_, wechatErr := pay.WechatPay{}.V3CloseOrder(ctx, outTradeNo)
						if wechatErr != nil {
							logx.Error(context.Background(), "微信V3关闭订单失败")
						}
					}
				}
			}
		}
	} else {
		// 微信支付 -- [ 向微信关闭订单或退款 ]
		if _, ok := PaymentTypeMap["1"]; ok {
			// 订单结束[end_type=2]实际已支付 执行自动退款
			if endType == "2" { // 0出货失败 1出货成功 2订单结束(自己取消)
				WechatPayConfig, _ := dao.Payment{}.GetWechatPay(context.Background(), dal.Q, RobotInfo.MerchantID, PaymentTypeMap["1"])
				if len(WechatPayConfig) != 0 {
					err = pay.WechatPay{}.ConfigInit(WechatPayConfig[0].WeChatAppID, WechatPayConfig[0].WeChatMchID,
						WechatPayConfig[0].WeChatAppKey, WechatPayConfig[0].CertFile)
					if err != nil {
						logx.Error(context.Background(), "ConfigInit error", logx.Err(err))
					}
					wechatRsp, wechatErr := pay.WechatPay{}.OrderClose(outTradeNo)
					if wechatErr != nil {
						err = errorx.New("微信关闭订单失败", -1)
						logx.Error(context.Background(), "wechat OrderClose error", logx.Err(wechatErr))
					}
					Response.WechatPayResult = wechatRsp
				} else {
					logx.Error(context.Background(), "微信没有配置具体的支付参数")
				}
			}
		}
		// 支付宝支付 [向支付宝关闭订单或退款]
		if _, ok := PaymentTypeMap["2"]; ok {
			AliPayConfig, _ := dao.Payment{}.GetAliPay(context.Background(), dal.Q, RobotInfo.MerchantID, PaymentTypeMap["2"])
			if len(AliPayConfig) != 0 {
				err1 := pay.Alipay{}.ConfigInit(AliPayConfig[0].AliPayAppID, AliPayConfig[0].AliPayPrivateKey)
				if err1 != nil {
					err = errorx.New("支付宝配置错误", -1)
					logx.Error(ctx, "支付宝配置错误")
				}
				aliRsp, aliErr := pay.Alipay{}.AliPayQuery(outTradeNo)
				logx.Info(ctx, "支付宝查询订单结果", logx.Any("alipay res", aliRsp))
				if aliErr != nil {
					logx.Error(ctx, "支付宝查询订单结果错误", logx.String("err", aliErr.Error()))
				}
				// 订单结束[end_type=2]实际已支付，执行自动退款
				if endType == "2" { // 0出货失败 1出货成功 2订单结束(自己取消)
					alipayRsp, alipayErr := pay.Alipay{}.AliPayClose(outTradeNo)
					logx.Info(ctx, "支付宝关闭订单结果", logx.Any("alipay res", alipayRsp))
					if alipayErr != nil {
						logx.Error(ctx, "支付宝关闭订单结果", logx.Any("alipay error", alipayErr.Error()))
						err = errorx.New("支付宝关闭订单失败", -1)
					}
					Response.AliPayResult = alipayRsp
				}
			} else {
				logx.Error(context.Background(), "支付宝没有配置具体的支付参数")
			}
		}
		// 小天才 -- [ 向微信关闭订单或退款 ]
		if _, ok := PaymentTypeMap["12"]; ok {
			// 订单结束[end_type=2]实际已支付 执行自动退款
			if endType == "2" { // 0出货失败 1出货成功 2订单结束(自己取消)
				WechatPayConfig, _ := dao.Payment{}.GetXiaotiancaiPay(context.Background(), dal.Q, RobotInfo.MerchantID, PaymentTypeMap["12"])
				if len(WechatPayConfig) != 0 {
					err = pay.WechatPay{}.ConfigInit(WechatPayConfig[0].WeChatAppID, WechatPayConfig[0].WeChatMchID,
						WechatPayConfig[0].WeChatAppKey, WechatPayConfig[0].CertFile)
					if err != nil {
						logx.Error(context.Background(), "wechat ConfigInit error", logx.Err(err))
					}
					wechatRsp, wechatErr := pay.WechatPay{}.OrderClose(outTradeNo)
					if wechatErr != nil {
						err = errorx.New("微信关闭订单失败", -1)
						logx.Error(context.Background(), "微信关闭订单失败")
					}
					Response.WechatPayResult = wechatRsp
				} else {
					logx.Error(context.Background(), "微信没有配置具体的支付参数")
				}
			}
		}
		// 小程序微信支付
		if _, ok := PaymentTypeMap["16"]; ok {
			// 订单结束[end_type=2]实际已支付 执行自动退款
			if endType == "2" { // 0出货失败 1出货成功 2订单结束(自己取消)
				if OrderInfo.ActualAmount > 0 { // 实付金额大于0时 判断微信退款
					err1 := pay.WechatPay{}.V3ConfigInit("", "", "")
					if err1 != nil {
						logx.Error(context.Background(), "WechatPay V3ConfigInit error", logx.Err(err1))
					} else {
						_, wechatErr := pay.WechatPay{}.V3CloseOrder(ctx, outTradeNo)
						if wechatErr != nil {
							logx.Error(context.Background(), "微信V3关闭订单失败")
						}
					}
				}
			}
		}
		// 保存支付结果
		WechatPayResultBytes, _ := json.Marshal(Response.WechatPayResult)
		AliPayResultBytes, _ := json.Marshal(Response.AliPayResult)
		// MaOrderDetails 更新订单查询结果
		db.Model(&models.MaOrderDetails{}).
			Where("id = ?", OrderInfo.OrderID).
			Updates(map[string]interface{}{
				"wechat_query_result": string(WechatPayResultBytes),
				"alipay_query_result": string(AliPayResultBytes),
				"modify_time":         time.Now(),
			})
	}

	if endType == "1" {
		// 针对多次关闭订单的情况：比如先触发未支付，后出货的情况（发生了积分退款），进行再次扣除积分
		func() {
			OrderPointsInfo, _ := dao.OrderPoints{}.Detail(ctx, db, OrderInfo.OutTradeNo, 2)
			if OrderPointsInfo != nil && OrderPointsInfo.OutTradeNo == OrderInfo.OutTradeNo {
				// 总订单数  >= 1
				var orderCount int64
				db.Model(&models.MaOrder{}).
					Where("out_trade_no = ?", OrderInfo.OutTradeNo).
					Count(&orderCount)
				// 退款订单数 == 0
				var refundOrderCount int64
				db.Model(&models.MaOrder{}).
					Where("out_trade_no = ?", OrderInfo.OutTradeNo).
					Where("order_status = 6"). // 退款
					Count(&refundOrderCount)
				if refundOrderCount < orderCount {
					var TotalPoints float64
					// 整单退
					// 如果传主订单号 || 订单总数 == 1
					if outTradeNo == OrderInfo.OutTradeNo || orderCount == 1 {
						TotalPoints = OrderPointsInfo.Points
					} else {
						// 分单退
						points := common.AvgDivide(OrderPointsInfo.Points, int(orderCount), 0)
						if refundOrderCount == orderCount-1 { // 最后一个订单退款
							TotalPoints = points[orderCount-1]
						} else {
							TotalPoints = points[0]
						}
					}
					logx.Error(ctx, "积分路过记录，临时测试5-1",
						logx.Any("out_trade_no", outTradeNo),
					)
					RobotLocationInfo, _ := service.OrderPoints{}.GetRobotLocationInfo(ctx, db, OrderPointsInfo.SalesID)
					tradeErr := points.NewPoints(RobotLocationInfo.PointsId).PointsConsume(ctx, db, salesID, OrderPointsInfo.OpenID, OrderInfo.OutTradeNo, -TotalPoints, RobotLocationInfo.PointsMallId)
					if tradeErr != nil {
						err = tradeErr
						return
					}
					// 订单积分再次消费记录
					dao.OrderPoints{}.Create(ctx, db, salesID, OrderInfo.OutTradeNo, OrderPointsInfo.OpenID, 1, RobotLocationInfo.PointsId, -TotalPoints)

					logx.Error(ctx, "积分路过记录，临时测试6-1",
						logx.Any("out_trade_no", outTradeNo),
						logx.Any("tradeErr", tradeErr),
						logx.Any("total points", TotalPoints))
				}
			}
		}()
	}

	// 支付返回时，仅处理第三方订单状态关闭
	if endType == "2" {
		data := make(map[string]interface{})
		data["remarks"] = endRemarks
		data["modify_time"] = time.Now() // 订单更新时间
		db.Model(&models.MaOrder{}).
			Where("out_trade_no = ?", outTradeNo).
			Updates(&data)

		// 积分冲正
		func() {

			rdx := redis.NewDB()
			for {
				//6.11 加码锁（主码 2s）
				success, err := rdx.SetNX(context.Background(), define.RedisCloseOrderLock+OrderInfo.OutTradeNo, 1, 2*time.Second).Result()
				if !success || err != nil {
					time.Sleep(100 * time.Millisecond)
					continue
				}
				break
			}

			// 生成退款单号
			orderRefundNo, _ := helper.UUID{}.String()
			OrderPointsInfo, _ := dao.OrderPoints{}.Detail(ctx, db, OrderInfo.OutTradeNo, 1)
			if OrderPointsInfo != nil && OrderPointsInfo.OutTradeNo == OrderInfo.OutTradeNo {
				// 总订单数
				var orderCount int64
				db.Model(&models.MaOrder{}).
					Where("out_trade_no = ?", OrderInfo.OutTradeNo).
					Count(&orderCount)
				// 退款订单数
				var refundOrderCount int64
				db.Model(&models.MaOrder{}).
					Where("out_trade_no = ?", OrderInfo.OutTradeNo).
					Where("order_status = 6"). // 退款
					Count(&refundOrderCount)
				// 单一订单，回正记录必须为0
				var reverseCount int64
				db.Model(&models.MaOrderPoints{}).
					Where("out_trade_no = ?", OrderInfo.OutTradeNo).
					Where("type = 2"). // 回正
					Count(&reverseCount)

				if refundOrderCount < orderCount && reverseCount == 0 {
					var refundPoints float64
					// 整单退
					// 如果传主订单号 || 订单总数 == 1
					if outTradeNo == OrderInfo.OutTradeNo || orderCount == 1 {
						refundPoints = -OrderPointsInfo.Points
					} else {
						// 分单退
						points := common.AvgDivide(-OrderPointsInfo.Points, int(orderCount), 0)
						if refundOrderCount == orderCount-1 { // 最后一个订单退款
							refundPoints = points[orderCount-1]
						} else {
							refundPoints = points[0]
						}
					}
					logx.Error(ctx, "积分路过记录，临时测试5",
						logx.Any("out_trade_no", outTradeNo),
					)
					RobotLocationInfo, _ := service.OrderPoints{}.GetRobotLocationInfo(ctx, db, OrderPointsInfo.SalesID)
					tradeErr := points.NewPoints(OrderPointsInfo.PointsID).PointsReverse(
						ctx,
						db,
						OrderPointsInfo.SalesID,        // 1. 机器识别码
						OrderPointsInfo.OpenID,         // 2. OpenID
						orderRefundNo,                  // 3. 退款单号
						OrderPointsInfo.OutTradeNo,     // 4. 订单号
						refundPoints,                   // 5. 需要退款的金额（正数）
						RobotLocationInfo.PointsMallId, // 6. 积分对接的商场ID
					)
					logx.Error(ctx, "积分路过记录，临时测试6",
						logx.Any("out_trade_no", outTradeNo),
						logx.Any("tradeErr", tradeErr),
						logx.Any("refundPoints", refundPoints),
					)
					if tradeErr != nil {
						// refundCode = 0
						err = tradeErr
						return
					}
					// 订单积分冲正记录
					dao.OrderPoints{}.Create(ctx, db, OrderPointsInfo.SalesID, OrderPointsInfo.OutTradeNo, OrderPointsInfo.OpenID, 2, OrderPointsInfo.PointsID, refundPoints)
				}
			}
		}()
	} else {
		if origialOutTradeNo != "" { // 赠送订单,关闭原订单
			outTradeNo = origialOutTradeNo
		}
		err1 := order.OrderService{}.CloseOrder(
			ctx,
			db,
			RobotInfo.ID,
			salesID,
			outTradeNo,
			endType,
			popUp,
			errorCode,
			endRemarks,
			logMsg,
			pushData,
		)
		if err1 != nil {
			logx.Error(context.Background(), "关闭订单失败", logx.String("outTradeNo", outTradeNo))
		}
	}
	// 更新机器运营日报
	service.RobotDailyReport{}.UpdateOrder(ctx, db, RobotInfo.RobotName, carbon.Now().ToDateString())

	// 更新商品运营日报
	/*
		var orderGoodsList []models.MaOrderGoods
		db.Model(&models.MaOrderGoods{}).
			Where("out_trade_no = ?", OrderInfo.OutTradeNo).
			Find(&orderGoodsList)
		for _, v := range orderGoodsList {
			service.GoodsDailyReport{}.UpdateOrderRefund(context.Background(), db, v.GoodsID, v.GoodsName, carbon.Now().ToDateString())
		}*/
	return
}

// GetSwitch 获取开关
func (r RobotService) GetSwitch(ctx context.Context, merchantID, salesID string) (SwitchList struct {
	Switch1 string `json:"switch1"`
	Switch2 string `json:"switch2"`
	Switch3 string `json:"switch3"`
	Switch4 string `json:"switch4"`
	Switch5 string `json:"switch5"`
}, err error,
) {
	db := mysql.NewDB()
	RobotInfo := models.MaRobot{}
	db.Model(&models.MaRobot{}).Where("sales_id = ?", salesID).Select("sales_id,robot_name,switch").Take(&RobotInfo)
	if RobotInfo.ID == "" {
		err = errorx.New("机器不存在", -1)
		return
	}
	json.Unmarshal([]byte(RobotInfo.Switch), &SwitchList)
	return
}

// SetSwitch 设置开关
func (r RobotService) SetSwitch(ctx context.Context, merchantID, salesID string, Switch struct {
	Switch1 string `json:"switch1"`
	Switch2 string `json:"switch2"`
	Switch3 string `json:"switch3"`
	Switch4 string `json:"switch4"`
	Switch5 string `json:"switch5"`
},
) (err error) {
	db := mysql.NewDB()
	var RobotCount int64
	db.Model(&models.MaRobot{}).
		Where("sales_id = ?", salesID).
		Count(&RobotCount)
	if RobotCount == 0 {
		err = errorx.New("机器不存在", -1)
		return
	}
	switchBytes, _ := json.Marshal(Switch)
	db.Model(&models.MaRobot{}).
		Where("sales_id = ?", salesID).
		UpdateColumn("switch", string(switchBytes))
	return
}

// GetAPK 查询版本号
func (r RobotService) GetAPK(ctx context.Context, merchantID string, versionCode int) (rsp models.MaRobotUpdateApk, err error) {
	db := mysql.NewDB()
	var ApkInfo models.MaRobotUpdateApk
	var ApkCount int64
	db.Model(&models.MaRobotUpdateApk{}).
		Order("version_code desc").Take(&ApkInfo).Count(&ApkCount)
	if ApkCount == 0 || versionCode > ApkInfo.VersionCode {
		err = errorx.New("查询错误", -1)
		return
	}
	return ApkInfo, nil
}

// ErrorReport 错误上报
func (r RobotService) ErrorReport(ctx context.Context, salesID, logMsg, errorCode, outTradeNo string) (err error) {
	db := mysql.NewDB()
	var RobotCount int64
	var RobotInfo models.MaRobot
	db.Model(&models.MaRobot{}).
		Where("sales_id = ?", salesID).
		Take(&RobotInfo).
		Count(&RobotCount)
	if RobotCount == 0 {
		err = errorx.New("机器不存在", -1)
		return
	}
	// 写入机器错误日志
	var InsertData models.MaRobotErrorLog
	InsertData.ID = RobotInfo.ID
	InsertData.SalesID = salesID
	InsertData.ErrorCode = errorCode
	InsertData.LogMsg = logMsg
	InsertData.OutTradeNo = outTradeNo
	InsertData.AddTime = time.Now()
	count := db.Model(&models.MaRobotErrorLog{}).Create(&InsertData).RowsAffected
	if count <= 0 {
		err = errorx.New("上报失败", -1)
		return
	}
	if errorCode == "31" {
		// 系统异常
		alarmMsg := map[string]interface{}{
			"alarm_type": define.ReportAlarmCode["31"],
			"robot_id":   RobotInfo.RobotName,
			"battery":    "",
			"created_at": time.Now().Unix(),
		}
		jsonString, _ := json.Marshal(alarmMsg)
		rdb := redis.NewDB()
		rdb.LPush(ctx, define.RedisAlarmNotifyList, string(jsonString))
	} else if errorCode == "32" {
		// 关舱超时
		alarmMsg := map[string]interface{}{
			"alarm_type": define.ReportAlarmCode["32"],
			"robot_id":   RobotInfo.RobotName,
			"battery":    "",
			"created_at": time.Now().Unix(),
		}
		// 标记订单的end为3[触发了出货过程],避免被判定为出货无响应
		// 通过本地缓存限制
		{
			mysql.NewDB().Model(&models.MaOrder{}).
				Where("out_trade_no = ?", outTradeNo).
				Update("end", 3)
		}
		jsonString, _ := json.Marshal(alarmMsg)
		rdb := redis.NewDB()
		rdb.LPush(ctx, define.RedisAlarmNotifyList, string(jsonString))
	} else if errorCode == "33" {
		// 初始化错误
		alarmMsg := map[string]interface{}{
			"alarm_type": define.ReportAlarmCode["33"],
			"robot_id":   RobotInfo.RobotName,
			"battery":    "",
			"created_at": time.Now().Unix(),
		}
		jsonString, _ := json.Marshal(alarmMsg)
		rdb := redis.NewDB()
		rdb.LPush(ctx, define.RedisAlarmNotifyList, string(jsonString))
	} else if errorCode == "101" {
		// 货门未关闭通知 | 出货舱门未关闭
		alarmMsg := map[string]interface{}{
			"alarm_type": define.ReportAlarmCode["101"],
			"robot_id":   RobotInfo.RobotName,
			"battery":    "",
			"created_at": time.Now().Unix(),
		}
		jsonString, _ := json.Marshal(alarmMsg)
		rdb := redis.NewDB()
		rdb.LPush(ctx, define.RedisAlarmNotifyList, string(jsonString))
	} else if errorCode == "102" {
		// 停止售卖 客户端主动
		alarmMsg := map[string]interface{}{
			"alarm_type": define.ReportAlarmCode["102"],
			"robot_id":   RobotInfo.RobotName,
			"battery":    "",
			"created_at": time.Now().Unix(),
		}
		jsonString, _ := json.Marshal(alarmMsg)
		rdb := redis.NewDB()
		rdb.LPush(ctx, define.RedisAlarmNotifyList, string(jsonString))
	} else if errorCode == "103" {
		// 黑屏重启超过5次 上报异常
		alarmMsg := map[string]interface{}{
			"alarm_type": define.ReportAlarmCode["103"],
			"robot_id":   RobotInfo.RobotName,
			"battery":    "",
			"created_at": time.Now().Unix(),
			"content":    logMsg, //增加黑屏告警的硬件版本信息
		}
		jsonString, _ := json.Marshal(alarmMsg)
		rdb := redis.NewDB()
		rdb.LPush(ctx, define.RedisAlarmNotifyList, string(jsonString))
	} else if errorCode == "104" {
		// 大内存告警
		alarmMsg := map[string]interface{}{
			"alarm_type": define.ReportAlarmCode[errorCode],
			"robot_id":   RobotInfo.RobotName,
			"battery":    "",
			"created_at": time.Now().Unix(),
			"content":    logMsg, //增加黑屏告警的硬件版本信息
		}
		jsonString, _ := json.Marshal(alarmMsg)
		rdb := redis.NewDB()
		rdb.LPush(ctx, define.RedisAlarmNotifyList, string(jsonString))
	} else if errorCode == "105" {
		// 97本地存储不足
		alarmMsg := map[string]interface{}{
			"alarm_type": define.ReportAlarmCode[errorCode],
			"robot_id":   RobotInfo.RobotName,
			"battery":    "",
			"created_at": time.Now().Unix(),
			"content":    logMsg, //增加黑屏告警的硬件版本信息
		}
		jsonString, _ := json.Marshal(alarmMsg)
		rdb := redis.NewDB()
		rdb.LPush(ctx, define.RedisAlarmNotifyList, string(jsonString))
	} else if errorCode == "106" {
		// 98本地存储不足
		alarmMsg := map[string]interface{}{
			"alarm_type": define.ReportAlarmCode[errorCode],
			"robot_id":   RobotInfo.RobotName,
			"battery":    "",
			"created_at": time.Now().Unix(),
			"content":    logMsg, //增加黑屏告警的硬件版本信息
		}
		jsonString, _ := json.Marshal(alarmMsg)
		rdb := redis.NewDB()
		rdb.LPush(ctx, define.RedisAlarmNotifyList, string(jsonString))
	}
	return
}

// ErrorReportPallets 货盘检测错误上报
func (r RobotService) ErrorReportPallets(ctx context.Context, merchantID, salesID string, Type, result, errorTimer int, outTradeNo, log, originaImagePath, inputImagePath string) (err error) {
	db := mysql.NewDB()
	var RobotCount int64
	var RobotInfo models.MaRobot
	db.Model(&models.MaRobot{}).
		Where("sales_id = ?", salesID).
		Take(&RobotInfo).
		Count(&RobotCount)
	if RobotCount == 0 {
		err = errorx.New("机器不存在", -1)
		return
	}
	// 写入日志
	var LogData models.MaRobotErrorLogPallets
	LogData.ID = RobotInfo.ID
	LogData.SalesID = salesID
	LogData.ErrorTimer = errorTimer
	LogData.OutTradeNo = outTradeNo // 新增订单号
	LogData.OriginaImagePath = originaImagePath
	LogData.InputImagePath = inputImagePath
	LogData.Type = Type
	LogData.Result = result
	LogData.AddTime = time.Now()
	LogData.Log = log
	db.Model(&models.MaRobotErrorLogPallets{}).Create(&LogData)
	return
}

type ErrorLogPalletsRes struct {
	models.MaRobotErrorLogPallets
	RobotName        string `json:"robot_name"`
	InputImagePath   string `json:"input_image_path"`
	OriginaImagePath string `json:"origina_image_path"`
	Type             string `json:"type"`
	OriType          int    `json:"ori_type"`
}

// GetErrorPallets 获取货盘检测错误记录
func (r RobotService) GetErrorPallets(ctx context.Context, db *gorm.DB, startTime, endTime, result string, Type, page, limit int) (data []ErrorLogPalletsRes, count int64, err error) {
	tx := db.Table(models.MaRobotErrorLogPallets{}.TableName() + " as e")

	if result != "" {
		tx.Where("e.result = ?", result)
	}

	if Type != 0 {
		tx.Where("e.type = ?", Type)
	}

	if startTime != "" && endTime != "" {
		tx.Where("e.add_time between ? and ?", startTime, endTime+" 23:59:59")
	}

	tx.WithContext(ctx).Count(&count)

	tx.Joins("left join " + models.MaRobot{}.TableName() + " as r on r.sales_id = e.sales_id")

	Res := []ErrorLogPalletsRes{}
	// 原始图片和出货图片互换
	field := "e.*, r.robot_name, e.origina_image_path AS input_image_path, e.input_image_path AS origina_image_path," +
		"concat((CASE e.type WHEN 0 THEN '0(初始化)' WHEN 2 THEN '2(出货前检测)' ELSE '未知' END), '_', e.type) AS type," +
		"e.type AS ori_type"
	helper.Gorm{}.Paginate(tx, page, limit).
		Select(field).Order("id desc").Find(&Res)
	return Res, count, err
}

// SetPaymentShowType sets the payment display type for a robot
func (r RobotService) SetPaymentShowType(ctx context.Context, salesID string, paymentType int) (count int64, err error) {
	db := mysql.NewDB()

	// Check if robot exists
	_, robotExist := r.CheckRobotExist(ctx, db, salesID, "")
	if !robotExist {
		err = errorx.New("找不到机器人", -1)
		return
	}

	// Update the payment show type
	updateData := map[string]interface{}{
		"payment_show_type": paymentType, // Use the provided payment type
	}

	count = db.Model(&models.MaRobot{}).
		Where("sales_id = ?", salesID).
		Updates(updateData).RowsAffected

	if count < 0 {
		err = errorx.New("操作失败", -1)
		return
	}

	return count, nil
}

// GetPaymentShowType gets the payment display type for a robot by sales ID
func (r RobotService) GetPaymentShowType(ctx context.Context, salesID string) (paymentType int, err error) {
	db := mysql.NewDB()

	// Check if robot exists
	_, robotExist := r.CheckRobotExist(ctx, db, salesID, "")
	if !robotExist {
		err = errorx.New("找不到机器人", -1)
		return
	}

	// Get the payment show type
	var robot models.MaRobot
	err = db.Model(&models.MaRobot{}).
		Where("sales_id = ?", salesID).
		Select("payment_show_type").
		Take(&robot).Error

	if err != nil {
		err = errorx.New("获取支付显示类型失败", -1)
		return
	}

	return robot.PaymentShowType, nil
}
